﻿using System;
using System.Collections.Generic;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Daybreak.Core.Rendering.Pipeline;

namespace Daybreak.Core.SceneGraph
{
    public class Camera
    {
        public Matrix View;
        public Matrix Proj;
        public Matrix rotationMatrix;

        public Vector3 Position = Vector3.Zero;
        public Vector3 rotation = Vector3.Zero;
        public Vector3 upVector = Vector3.Zero;
        Vector3 targetPos = Vector3.Zero;

        public Vector3 direction = Vector3.Forward;

        public BoundingFrustum Frustum;
        public BoundingSphere FrustumSphere;

        public float aspectRatio = 0f;
        public float fov = (float)Math.PI / 4.0f;
        public float NearPlane = 1;
        public float FarPlane = 5000;

        public State state = State.FirstPerson;
        public State lastState = State.FirstPerson;

        public enum State
        {
            FirstPerson = 0,
            ThirdPerson,
            Fixed
        }

        public float speed = 2f;

        public Camera()
        {
            aspectRatio = (float)Engine.GraphicsDevice.Viewport.Width / (float)Engine.GraphicsDevice.Viewport.Height;
            Frustum = new BoundingFrustum(Matrix.Identity);

            rotation.Y = MathHelper.ToRadians(180);
        }

        public virtual void Update(float delta)
        {
            UpdateMatrices();
        }

        public void SetPosition(Vector3 newPosition)
        {
            Position = newPosition;
        }

        public virtual void UpdateMatrices()
        {
            rotationMatrix = Matrix.CreateRotationX(rotation.X) * Matrix.CreateRotationY(rotation.Y);
            targetPos = Position + Vector3.Transform(new Vector3(0, 0, 1), rotationMatrix);
            upVector = Vector3.Transform(new Vector3(0, 1, 0), rotationMatrix);

            direction = Vector3.Normalize(targetPos - Position);

            View = Matrix.CreateLookAt(Position, targetPos, upVector);
            Proj = Matrix.CreatePerspectiveFieldOfView(fov, aspectRatio, NearPlane, FarPlane);

            Frustum.Matrix = View * Proj;
            FrustumSphere = BoundingSphere.CreateFromFrustum(Frustum);
        }

        public void ToggleNextState()
        {
            switch (state)
            {
                case State.FirstPerson:
                    state = State.Fixed;
                    break;
                case State.Fixed:
                    state = State.FirstPerson; //Temp
                    break;
                case State.ThirdPerson:
                    state = State.FirstPerson;
                    break;
            }
        }

        public void SetState(State newState)
        {
            state = newState;
        }

        public Vector2 RotationDegrees
        {
            get { return new Vector2(MathHelper.ToDegrees(rotation.X), MathHelper.ToDegrees(rotation.Y));}
            set 
            {
                rotation.X = MathHelper.ToRadians(value.X);
                rotation.Y = MathHelper.ToRadians(value.Y);
            }
        }

        internal void UpdateFrameChanges(Pipes.FrameContext frameContext)
        {
            Sas.View view = frameContext.GetAttribute<Sas.View>(SceneObject.Any);

            view.Value = View;

            Sas.Projection proj = frameContext.GetAttribute<Sas.Projection>(SceneObject.Any);

            proj.Value = Proj;

            Shared<Sas.ViewProjection>.GetAttribute(SceneObject.Any).Update(view, proj);
            Shared<Sas.ViewInverse>.GetAttribute(SceneObject.Any).Update(view);

            Sas.CameraPosition camPos = frameContext.GetAttribute<Sas.CameraPosition>(SceneObject.Any);

            camPos.Value = Position; //Matrix.Invert(camera.View).Translation;

        }
    }

    public class MouseCamera : Camera
    {
        private KeyboardState keys;
        private MouseState currentMouseState, previousMouseState;

        Point screenCenter = Point.Zero;
        public Vector3 lastPosition;
        bool bToggleKeyDown = false;

        public float SpeedFactor = 4;

        public MouseCamera()
        {
            screenCenter.X = Engine.GraphicsDevice.Viewport.Width / 2;
            screenCenter.Y = Engine.GraphicsDevice.Viewport.Height / 2;

            Reset();
        }

        public MouseCamera(float speed):this()
        {
            SpeedFactor = speed;
        }

        public void Reset()
        {
            Mouse.SetPosition(screenCenter.X, screenCenter.Y);
            currentMouseState = Mouse.GetState();
            previousMouseState = Mouse.GetState();
        }

        public override void Update(float delta)
        {
            PollInput(delta * 30);

            base.Update(delta);
        }

        private void PollInput(float amountOfMovement)
        {
            keys = Keyboard.GetState();
            currentMouseState = Mouse.GetState();

            if (state != State.Fixed)
            {
                Vector3 moveVector = new Vector3();

                if (state == State.FirstPerson)
                {
                    if (keys.IsKeyDown(Keys.Right) || keys.IsKeyDown(Keys.D))
                        moveVector.X -= amountOfMovement;
                    if (keys.IsKeyDown(Keys.Left) || keys.IsKeyDown(Keys.A))
                        moveVector.X += amountOfMovement;
                    if (keys.IsKeyDown(Keys.Down) || keys.IsKeyDown(Keys.S))
                        moveVector.Z -= amountOfMovement*SpeedFactor;
                    if (keys.IsKeyDown(Keys.Up) || keys.IsKeyDown(Keys.W))
                        moveVector.Z += amountOfMovement*SpeedFactor;
                }

                lastPosition = Position;
                rotationMatrix = Matrix.CreateRotationX(rotation.X) * Matrix.CreateRotationY(rotation.Y);
                Position += Vector3.Transform(moveVector, rotationMatrix);

                speed = Vector3.Distance(Position, lastPosition);

                if (currentMouseState.X != previousMouseState.X)
                    rotation.Y -= amountOfMovement / 800.0f * (currentMouseState.X - previousMouseState.X);
                if (currentMouseState.Y != previousMouseState.Y)
                    rotation.X += amountOfMovement / 800.0f * (currentMouseState.Y - previousMouseState.Y);

                Mouse.SetPosition(screenCenter.X, screenCenter.Y);
            }

            if (keys.IsKeyDown(Keys.C))
            {
                if (!bToggleKeyDown)
                {
                    bToggleKeyDown = true;
                    Mouse.SetPosition(screenCenter.X, screenCenter.Y);
                    ToggleNextState();
                }
            }
            else if (bToggleKeyDown)
                bToggleKeyDown = false;
        }
    }

}
